﻿using System;
using CoreTypes;
using System.IO;
using System.Net;
using System.Linq;
using System.Custom;
using System.Xml.Linq;
using System.Windows.Forms;
using MainModule.Properties;

namespace MainModule
{
   public enum InstallationState : int
   {
      Error = 0xFE,
      NoUpdates = 0xF0,
      InstallReady = 0xF1,
      ForcedInstall = 0xF2,
      CheckInProgress = 0xF3,
      SubmitInfoSuccess = 0xF4,
   }

   public enum CheckInstallationArg
   {
      Info,
      Update,
      Culture,
   }

   public class InstallationCheckEventArgs : EventArgs
   {
      public Exception Error;

      public InstallationState State;

      public InstallationCheckEventArgs(InstallationState state)
      {
         State = state;
      }

      public InstallationCheckEventArgs(Exception error)
      {
         Error = error;
         State = InstallationState.Error;
      }
   }

   public delegate void CheckInstallationDelegate(object sender, InstallationCheckEventArgs e);

   public static class Deployment
   {
      class UpdateManifest
      {
         public bool AllowRevert;
         public bool ForcedInstall;
         public string Description;
         public Version LatestVersion;

         public UpdateManifest(XElement xml)
         {
            AllowRevert = true;

            XAttribute xLastVersion = xml.Attribute("lastVersion");
            XAttribute xDescription = xml.Attribute("desc");
            XAttribute xAllowRevert = xml.Attribute("allowRevert");
            XAttribute xForcedInstall = xml.Attribute("forcedInstall");

            if (xAllowRevert != null) AllowRevert = (bool)xAllowRevert;
            if (xDescription != null) Description = (string)xDescription;
            if (xForcedInstall != null) ForcedInstall = (bool)xForcedInstall;
            if (xLastVersion != null) LatestVersion = new Version((string)xLastVersion);
         }
      }

      static string FTmpDir;
      static string FBackupDir;
      static string FUpdatesDir;
      static string FCurrentFilePath;
      static string FNewInstallerPath;
      static string FTmpInstallerPath;
      static string FInstallerFileName;
      static string FBackupInstallerPath;

      static Deployment()
      {
         FInstallerFileName = InternetCommunicator._AppName + ".exe";
         FTmpDir = Environment.GetEnvironmentVariable("TMP", EnvironmentVariableTarget.User);
         FBackupDir = FTmpDir + "\\" + InternetCommunicator._AppName + "Backup";
         FUpdatesDir = FTmpDir + "\\" + InternetCommunicator._AppName + "Updates";
         FCurrentFilePath = Application.ExecutablePath.ToLower();
         FNewInstallerPath = FUpdatesDir + "\\" + FInstallerFileName;
         FBackupInstallerPath = FBackupDir + "\\" + FInstallerFileName;
         FTmpInstallerPath = FNewInstallerPath + ".new";
      }

      static bool UpdatesAvailable()
      {
         int result = Shell.CompareVersions(FCurrentFilePath, FNewInstallerPath);

         //if (result == 0) result = File.GetLastWriteTimeUtc(FThisFilePath).CompareTo(File.GetLastWriteTimeUtc(FNewFilePath));

         return result < 0;
      }

      public static InstallationState SubmitInstallInfo()
      {
         try
         {
            WebClient wc = new WebClient();

            DateTime cts = Directory.GetCreationTime(Path.GetDirectoryName(FCurrentFilePath));
            DateTime sts = Directory.GetCreationTime(Environment.GetFolderPath(Environment.SpecialFolder.Desktop));

            if (Program.CheckInstallationMode) InternetCommunicator.Self.LoadProfile(null);

            string[] svcList = InternetCommunicator.Self.Accounts.Select(a => a.ServiceName).ToArray();

            string url = Settings.Default.BaseUrl + "/count.php" +
                                  "?cts=" + cts.ToString("yyyy-MM-dd HH:mm:ss") +
                                  "&sts=" + sts.ToString("yyyy-MM-dd HH:mm:ss") +
                                  "&sl=" + (svcList.Length > 0 ? string.Join(";", svcList) : "") +
                                  "&os=" + Computer.OSVersion +
                                  "&fx=" + Environment.Version +
                                  "&v=" + InternetCommunicator.AppInfo.ProductVersion +
                                  "&ok=" + (Settings.Default.CountOK ? 1 : 0);
            
            string reply = wc.DownloadString(url);

            wc.Dispose();

            return !string.IsNullOrEmpty(reply) && reply.StartsWith("OK") ? InstallationState.SubmitInfoSuccess : InstallationState.Error;
         }
         catch
         {
            return InstallationState.Error;
         }
      }

      public static InstallationState DownloadUpdates()
      {
         if (SubmitInstallInfo() == InstallationState.Error) return InstallationState.Error;

         UpdateManifest updateManifest = new UpdateManifest(XElement.Load(Settings.Default.BaseUrl + "/updates/update.manifest"));

         Version thisVersion = new Version(Application.ProductVersion);
         Version localNewVersion = Shell.GetAssemblyVersion(FNewInstallerPath);

         if (thisVersion.CompareTo(localNewVersion) < 0 &&
             updateManifest.LatestVersion.CompareTo(localNewVersion) == 0)
         {
            return InstallationState.InstallReady;
         }
         else if (thisVersion.CompareTo(updateManifest.LatestVersion) < 0)
         {
            Directory.CreateDirectory(FUpdatesDir);

            WebClient wc = new WebClient();

            wc.DownloadFile(Settings.Default.BaseUrl + "/download.php?target=" +
                           (Settings.Default.LocalNetwork ? "update_lan" : "update"), FTmpInstallerPath);
            wc.Dispose();

            if (updateManifest.AllowRevert)
            {
               Directory.CreateDirectory(Path.GetDirectoryName(FBackupInstallerPath));

               File.Delete(FBackupInstallerPath);
               if (File.Exists(FNewInstallerPath)) File.Move(FNewInstallerPath, FBackupInstallerPath);
               File.Move(FTmpInstallerPath, FNewInstallerPath);
            }

            return updateManifest.ForcedInstall ? InstallationState.ForcedInstall : InstallationState.InstallReady;
         }
         else
         {
            return InstallationState.NoUpdates;
         }
      }

      public static bool InstallUpdatesIfAvailable()
      {
         return UpdatesAvailable() ? Shell.RunProcess(FNewInstallerPath, " /silent", true) != null : false;
      }
   }
}
